package model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Random;

import controller.Spiel;

@SuppressWarnings("unchecked")
public abstract class Extra {
	public int y;
	public int x;
	public Color color;
	public int radius = 20;
	public boolean active;
	public int lifetime = 100;
	protected Spieler spieler;

	private static Class<?>[] classes;
	private static HashMap<Class<Object>, Color> colors;

	static {
		classes = Extra.class.getDeclaredClasses();
		Color[] colors = new Color[27];
		int i = 0;
		for (int r = 0; r <= 255; r += 255 / 2) {
			for (int g = 0; g <= 255; g += 127) {
				for (int b = 0; b <= 255; b += 127) {
					colors[i++] = new Color(r, g, b);
				}
			}
		}

		if (classes.length > colors.length)
			throw new RuntimeException("Zu wenige Farben definiert!");

		Collections.shuffle(Arrays.asList(colors));
		Extra.colors = new HashMap<Class<Object>, Color>();
		for (i = 0; i < classes.length; i++) {
			Extra.colors.put((Class<Object>) classes[i], colors[i]);
		}
	}

	public static Extra getRandomExtra() {
		int random = new Random().nextInt(classes.length);
		try {
			Extra e = (Extra) classes[random].newInstance();
			e.color = colors.get(classes[random]);
			return e;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return null;
	}

	public void doRunFirst(Spieler ausloeser) {
		this.spieler = ausloeser;
		this.active = true;
		this.runFirst(ausloeser);
	}

	public void runFirst(Spieler a) {
	}

	public void run() {
	}

	public void die() {
	}

	public void paint(Graphics2D g2) {
		if (this.active)
			this.paintActive(g2);
		else
			this.paintInactive(g2);
	}

	public void paintInactive(Graphics2D g2) {
		g2.setColor(color);
		g2.fillOval(x - radius / 2, y - radius / 2, radius, radius);
	}

	public void paintActive(Graphics2D g2) {
	}

	protected void removeCircle(int x, int y, int radius, boolean kill,
			boolean remove) {
		for (int i = -1; i <= 1; i++) {
			for (int j = -1; j <= 1; j++) {
				// Schritte entfernen
				if (remove) {
					synchronized (Spiel.CURRENT_SPIEL.spielfeld) {
						Spiel.CURRENT_SPIEL.spielfeld
								.removeCircle(x, y, radius);
						Spiel.CURRENT_SPIEL.frame.gamePanel
								.addDirtyRegion(new Rectangle(x - radius, y
										- radius, 2 * radius, 2 * radius));
					}
				}

				if (kill) {
					for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
						Linie l = s.getLinie();
						int distX = (int) (l.getX() - (x + Spiel.GAME_WIDTH * i));
						int distY = (int) (l.getX() - (y + Spiel.GAME_HEIGHT
								* i));
						if (!s.isTot()
								&& distX * distX + distY * distY <= radius
										* radius) {
							s.setTot(true);
							Spiel.CURRENT_SPIEL.logik.onSpielerTot(spieler);
						}
					}
				}
			}
		}
	}

	protected void moveTo(Spieler s, float x, float y) {
		Linie l = s.getLinie();
		l.lastSchritt = new Schritt(x, y, x, y, x, y, x, y,
				Schritt.LUECKE_COLOR);
	}

	static class RubberExtra extends Extra {
		public void run() {
			Linie l = this.spieler.getLinie();
			l.transform.luecke = true;
			int radius = (int) (this.lifetime > l.width * 5 ? l.width * 5
					: this.lifetime);
			removeCircle((int) l.getX(), (int) l.getY(), radius, false,
					lifetime % 5 == 0);
		}

		public void paintActive(Graphics2D g2) {
			g2.setColor(Color.RED);
			Linie l = this.spieler.getLinie();
			int radius = (int) (this.lifetime > l.width * 5 ? l.width * 5
					: this.lifetime);
			g2.drawOval((int) l.getX() - radius, (int) l.getY() - radius,
					2 * radius, 2 * radius);
		}
	}

	static class BombExtra extends Extra {
		public void run() {
			if (lifetime > 50)
				return;

			int radius = 250 - lifetime * 5;
			removeCircle(this.x, this.y, radius, true, lifetime == 1);
		}

		public void paintActive(Graphics2D g2) {
			g2.setColor(Color.RED);
			int radius = 250 - lifetime * 5;
			for (int dx = -1; dx <= 1; dx++) {
				for (int dy = -1; dy <= 1; dy++) {
					g2.fillOval(x + dx * Spiel.GAME_WIDTH - radius, y + dy
							* Spiel.GAME_HEIGHT - radius, 2 * radius,
							2 * radius);
				}
			}
		}
	}

	static class AllesWegExtra extends Extra {
		public void runFirst(Spieler ausloeser) {
			super.runFirst(ausloeser);

			removeCircle(0, 0, Spiel.GAME_WIDTH + Spiel.GAME_HEIGHT, false,
					true);
		}
	}

	static class AlleDickAusserSpielerExtra extends Extra {
		public void run() {
			for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
				if (s != this.spieler)
					s.getLinie().transform.widthFaktor.add(0.4f);
			}
		}
	}

	static class AlleDuennAusserSpielerExtra extends Extra {
		public void run() {
			for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
				if (s != this.spieler)
					s.getLinie().transform.widthFaktor.sub(0.4f);
			}
		}
	}

	static class SelbstDuennAusserSpielerExtra extends Extra {
		public void run() {
			this.spieler.getLinie().transform.widthFaktor.sub(0.4f);
		}
	}

	static class AlleSchnellAusserSpielerExtra extends Extra {
		public void run() {
			for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
				if (s != this.spieler)
					s.getLinie().transform.vFaktor.add(0.5f);
			}
		}
	}

	static class SelberSchnellExtra extends Extra {
		public void run() {
			this.spieler.getLinie().transform.vFaktor.add(0.5f);
		}
	}

	static class SelberEngerRadiusExtra extends Extra {
		public void run() {
			this.spieler.getLinie().transform.phiFaktor.add(0.5f);
		}
	}

	static class AndereWeiterRadiusExtra extends Extra {
		public void run() {
			for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
				if (s != this.spieler)
					s.getLinie().transform.phiFaktor.sub(0.2f);
			}
		}
	}

	static class LinienTauschenExtra extends Extra {
		public void runFirst(Spieler ausloeser) {
			super.runFirst(ausloeser);

			Spieler[] spieler = Spiel.CURRENT_SPIEL.getSpieler();
			int lebendigeSpieler = Spiel.CURRENT_SPIEL.logik
					.getLebendigeSpieler();
			Linie[] linien = new Linie[lebendigeSpieler];
			for (int i = 0, j = 0; i < spieler.length; i++, j++) {
				if (spieler[i].isTot()) {
					j--;
					continue;
				}
				linien[j] = spieler[i].getLinie();
			}

			Arrays.sort(linien, new Comparator<Linie>() {

				@Override
				public int compare(Linie arg0, Linie arg1) {
					return Math.random() < 0.5 ? -1 : 1;
				}
			});

			for (int i = 0, j = 0; i < spieler.length; i++, j++) {
				if (spieler[i].isTot()) {
					j--;
					continue;
				}
				spieler[i].setLinie(linien[j]);
			}
		}
	}

	static class AndereSinusGeschwindigkeitExtra extends Extra {
		public void run() {
			for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
				if (s != this.spieler)
					s.getLinie().transform.vFaktor.add((float) (0.5f * Math
							.sin(lifetime / 10.0)));
			}
		}
	}

	// static class MagnetExtra extends Extra {
	// public void run() {
	// for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
	// if (s.isTot())
	// continue;
	//
	// Linie l = s.getLinie();
	// float distanceSquare = (this.x * this.x + l.x * l.x - 2 * this.x * l.x)
	// + (this.y * this.y + l.y * l.y - 2 * this.y * l.y);
	// float force = 0.3f / (1 + distanceSquare / 1000000);
	// l.x += (l.x - this.x) / (l.y - this.y) * force;
	// l.y -= (l.y - this.y) / (l.x - this.x) * force;
	// }
	// }
	// }

	static class TeleporterExtra extends Extra {
		private Extra target;

		{
			this.target = new Extra() {
				{
					this.color = Extra.colors.get(TeleporterExtra.class);
				}

				public void runFirst(Spieler ausloeser) {
					super.runFirst(ausloeser);

					moveTo(ausloeser, TeleporterExtra.this.x,
							TeleporterExtra.this.y);

					this.lifetime = 0;
					this.active = true;
					TeleporterExtra.this.lifetime = 0;
					TeleporterExtra.this.active = true;
				}
			};
			this.target.x = Spiel.CURRENT_SPIEL.logik.getRandomInX(20);
			this.target.y = Spiel.CURRENT_SPIEL.logik.getRandomInY(20);

			Spiel.CURRENT_SPIEL.extras.add(target);
		}

		public void runFirst(Spieler ausloeser) {
			moveTo(ausloeser, this.target.x, this.target.y);

			this.lifetime = 0;
			this.active = true;
			this.target.lifetime = 0;
			this.target.active = true;
		}
	}

	static class RichtungsWechselExtra extends Extra {
		public void run() {
			for (Spieler s : Spiel.CURRENT_SPIEL.getSpieler()) {
				if (s != this.spieler) {
					s.getLinie().transform.phiFaktor.mul(-1);
					s.getLinie().transform.circleColor = Color.RED;
				}
			}
		}
	}

	static class VieleExtrasExtra extends Extra {
		public void run() {
			if (Math.random() < 1 / 18.0) {
				Extra e = Extra.getRandomExtra();
				e.x = Spiel.CURRENT_SPIEL.logik.getRandomInX(20);
				e.y = Spiel.CURRENT_SPIEL.logik.getRandomInY(20);
				Spiel.CURRENT_SPIEL.extras.add(e);
			}
		}
	}

	static class BGExtra extends Extra {
		public void runFirst(Spieler s) {
			Spiel.CURRENT_SPIEL.frame.gamePanel.setBackground(s.getFarbe());
		}

		public void die() {
			Spiel.CURRENT_SPIEL.frame.gamePanel.setBackground(Color.BLACK);
		}
	}

}
